package org.shiro.demo.controller;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;

import javax.annotation.Resource;
import javax.net.ssl.HttpsURLConnection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import open189.sign.SmsSender;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.joda.time.DateTime;
import org.joda.time.Minutes;
import org.joda.time.Seconds;
import org.shiro.demo.entity.ResponseResult;
import org.shiro.demo.entity.Sms;
import org.shiro.demo.entity.User;
import org.shiro.demo.service.IBaseService;
import org.shiro.demo.service.IUserService;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.utils.AESUtil;

/**
 * 手机短信验证。
 * 
 * ① 手机短信验证码 登陆 .
 * 
 * ② 手机短信验证码 注册.
 * 
 * ③ 手机短信验证码 找回密码.
 * 
 * 
 * 防止DDOS暴力破解： 短信登陆，短信注册，短信找回密码 十分钟不能连错5次，十分钟 连错5次 将冻结 验证接口。
 * 
 * 登陆的时候，先判断失败次数是否达到5次了： ①是： 走到第③步 ②不是：让其尝试登陆，走到第⑥步
 * 
 * ③判断失败时间是不是十分钟之内： ④是：不让登陆，提示冻结十分钟（以失败时间算起十分钟内为准）。 ⑤不是：计数器归零，让其尝试登陆。 走到第⑥步
 * 
 * ⑥一旦登陆失败，判断失败是否十分钟内： ⑦是：计数器累加。走到第⑨步 ⑧不是：计数器归1（注意是归1，不是加1）
 * 
 * ⑨计数器是否达到5 ⑩是：计数器达到5，更新到最新的失败时间。 11、不是： 计数器没达到5，保持原始的失败时间。
 * 
 * 
 *
 * 同一调用 method: SendSmsCode
 * 
 * @author chinesejie
 * 
 *         --暂时不用这个接口
 *
 * 访问接口https://oauth.api.189.cn/emp/oauth2/v3/access_token
 * 参数 
 * 
 * app_id:205195010000041437
 * 
 * app_secret:1a25420fdfd98c47b009f9c5cf762f4e
 *
 * grant_type:client_credentials
 *
 * btn_ccauth:认证授权
 * 返回{"res_code":"0","res_message":"Success","access_token":"df8722175e9f56319a2bf0596445cad31434854697577","expires_in":2592000}
 *
 *发送消息的时候 报错返回
 *报错返回：{"res_code" : 110,"res_message" : "request app [201286860000036675] ACCESS_TOKEN [23981d1080c6565f6a4471a42b4e41c21432299016239] not valid"}
 */
@Controller
@RequestMapping(value = "/sms")
public class SmsController {
	
	@Resource(name = "smsSender")
	SmsSender smsSender;
	
	@Resource(name = "userService")
	private IUserService userService;

	@Resource(name = "baseService")
	private IBaseService baseService;

	/**
	 * 手机登陆
	 * 
	 * 
	 * @param user
	 * @param session
	 * @param request
	 * @param reponse
	 * @return
	 */
	// @RequestMapping(value = "/smslogin")
	// public @ResponseBody Object smslogin(String phone, String code,
	// HttpSession session, HttpServletRequest request, HttpServletResponse
	// reponse) {
	//
	// if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)) {
	// return new ResponseResult(0, "手机号或者验证码不能为空", "");
	// }
	// // 查查看 是否 验证码 过期了
	// Sms sms = baseService.getById(Sms.class, phone);
	// if (sms == null || isExpired(sms.getCreateTime()) ||
	// !("Login").equals(sms.getType())) {
	// return new ResponseResult(0, "验证码无效", "");
	// }
	//
	// // 获取当前的Subject
	// Subject curUser = SecurityUtils.getSubject();
	// UsernamePasswordToken token = new UsernamePasswordToken("sms;" + phone,
	// code);
	// token.setRememberMe(true);
	// try {
	// //
	// 在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm执行必须的认证检查
	// // 每个Realm都能在必要时对提交的AuthenticationTokens作出反应
	// // 所以这一步在调用login(token)方法时,它会走到ShiroDbRealm.doGetAuthenticationInfo()方法中
	// System.out.println("1");
	// curUser.login(token);
	// System.out.println("2");
	// // 添加信息
	// /*
	// * if (null != curUser) { Session shiro_session =
	// * curUser.getSession(); if (null != shiro_session) {
	// * shiro_session.setAttribute(Keys.CURRENT_USER, user); } }
	// */
	// System.out.println("3");
	// System.out.println(request.getSession().getId());
	// reponse.addHeader("id", request.getSession().getId());
	// reponse.addHeader("Cookie", request.getHeader("Cookie"));
	// User user = userService.getByPhone(phone);
	// ResponseResult rr = new ResponseResult(1, request.getSession().getId(),
	// user);
	// // / 走到这一步， code 要重置.设置为空
	// sms.setCode("");
	// baseService.update(sms);
	// return rr;
	// } catch (AuthenticationException e) {
	// // 通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景
	// token.clear();
	// return new ResponseResult(0, "登录失败", "");// 登录失败，返回空
	// }
	// }

	/**
	 * 手机 注册
	 * 
	 * 
	 * @param user
	 * @param session
	 * @param request
	 * @param reponse
	 * @return
	 */
	@RequestMapping(value = "/smsregister")
	public @ResponseBody Object smsregister(String phone, String code, String password, HttpSession session, HttpServletRequest request, HttpServletResponse reponse) {
		String deString = null;
		try {
			deString = AESUtil.Decrypt(password);
			password = deString;
			if (deString == null) {
				return new ResponseResult(0, "密码解密失败", null);// 注册失败，返回空
			}
			if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(code) || StringUtils.isEmpty(password)) {
				return new ResponseResult(0, "手机号或者验证码不能为空", "");
			}
			// 密码 不能再简单
			if (password.length() <= 4) {
				return new ResponseResult(0, "密码太简单", "");
			}
			// 查查看 是否 验证码 过期了
			Sms sms = baseService.getById(Sms.class, phone);
			if (sms == null || isExpired(sms.getCreateTime()) || !(SmsType.Register.toString()).equals(sms.getType()) || !code.equals(sms.getCode())) {
				return new ResponseResult(0, "验证码无效", "");
			}
			if (userService.getByPhone(phone) != null) {
				return new ResponseResult(0, "用户已存在，若您是老用户，请返回首页找回密码。之前没有设置密码也请设置新密码。", "");
			}

			// 添加信息
			User user = new User();
			user.setPassword(password);
			user.setPhone(phone);
			user.setUsername(phone);
			user.setName(phone);
			user.setCreateTime(new Date());
			user.setBirthday(new DateTime(2000, 1, 1, 0, 0, 0).toDate());//修补bug 
			System.out.println("--注册成功");
			baseService.save(user);
			ResponseResult rr = new ResponseResult(1, "注册成功", user);
			// / 走到这一步， code 要重置.设置为空
			sms.setCode("");
			baseService.update(sms);
			return rr;
		} catch (Exception e) {
			e.printStackTrace();
			return new ResponseResult(0, "注册失败", "");// 注册失败，返回空
		}
	}

	/**
	 * 手机 找回密码
	 * 
	 * 
	 * @param user
	 * @param session
	 * @param request
	 * @param reponse
	 * @return
	 */
	@RequestMapping(value = "/getback")
	public @ResponseBody Object getback(String phone, String code, String password, HttpSession session, HttpServletRequest request, HttpServletResponse reponse) {
		String deString = null;
		try {
			deString = AESUtil.Decrypt(password);
			password = deString;
			if (deString == null) {
				return new ResponseResult(0, "密码解密失败", null);// 注册失败，返回空
			}
			if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(code) || StringUtils.isEmpty(password)) {
				return new ResponseResult(0, "手机号或者验证码或者密码不能为空", "");
			}
			// 密码 不能再简单
			if (password.length() <= 4) {
				return new ResponseResult(0, "密码太简单", "");
			}
			// 查查看 是否 验证码 过期了
			Sms sms = baseService.getById(Sms.class, phone);
			if (sms == null || isExpired(sms.getCreateTime()) || !(SmsType.Getback.toString()).equals(sms.getType()) || !code.equals(sms.getCode())) {
				return new ResponseResult(0, "验证码无效", "");
			}

			// 添加信息
			User user = userService.getByPhone(phone);
			System.out.println("--找回密码成功");
			user.setPassword(password);
			baseService.update(user);
			user.setPassword("");
			ResponseResult rr = new ResponseResult(1, "重置密码成功", user);
			// / 走到这一步， code 要重置.设置为空
			sms.setCode("");
			baseService.update(sms);
			return rr;
		} catch (Exception e) {
			e.printStackTrace();
			return new ResponseResult(0, "重置失败", "");// 登录失败，返回空
		}
	}

	enum SmsType {
		Login, Register, Getback
	}

	/**
	 * 发送验证码。。可能不需要用到。。如果使用 掌淘科技的话
	 * 
	 * type = Login Register Getback
	 * 
	 * @param request
	 * @param reponse
	 * @param phone
	 * @param pw
	 * @return
	 */
	@RequestMapping(value = "/send")
	@ResponseBody
	public ResponseResult send(HttpServletRequest request, HttpServletResponse reponse, @RequestParam(value = "phone", required = true) String phone, @RequestParam(value = "type", required = true) String type) {
		if (StringUtils.isEmpty(phone)) {
			return new ResponseResult(0, "手机不存在", null);
		}
		try {
			User user = userService.getByPhone(phone);
			switch (SmsType.valueOf(type)) {
			case Login:
				// 登陆验证码。。逻辑：
				/**
				 * ①先查看该用户是否已经注册，没注册返回 "未注册"。
				 * 
				 * ②注册了，就看上一次发送 验证码的时间， 如果在30s内，就警告。。
				 * 
				 * ③ 没有上述两个阻碍，就发送验证码
				 */
				if (user != null) {
					// 查看上一次 验证码是什么时候。
					Sms old = baseService.getById(Sms.class, phone);

					if (old != null && isRepeate(old.getCreateTime())) {// 重复了
																		// 30s未到。
						return new ResponseResult(0, "请等待30s之后再请求", null);
					}

					// 存入数据库
					// 取值范围是[1000,10000)
					int random = (int) (Math.random() * 10000) + 1000;
					Sms sms = new Sms();
					sms.setCode(String.valueOf(random));
					sms.setCreateTime(new Date());
					sms.setPhone(phone);
					sms.setType(SmsType.Login.toString());
					System.out.println("存入数据库了" + phone + ":" + random);
					System.out.println("发送登陆验证码" + phone + ":" + random);
					if (smsSender.postSMS(random + "", phone)) {
						baseService.update(sms);// 发送成功 才存数据库 
						return new ResponseResult(1, "验证码已发送，稍等", null);
					} else {
						return new ResponseResult(1, "验证码发送有问题", null);
					}
				} else {
					return new ResponseResult(0, "用户未注册，先去注册", null);
				}
			case Register:
				// 注册验证码。。逻辑：
				/**
				 * ①先查看该用户是否已经注册，已注册返回 "已注册"。
				 * 
				 * ②未注册了，就看上一次发送 验证码的时间， 如果在30s内，就警告。。
				 * 
				 * ③ 没有上述两个阻碍，就发送验证码
				 */
				if (user == null) {// 没有注册，ok 发送 验证码
					// 查看上一次 验证码是什么时候。
					Sms old = baseService.getById(Sms.class, phone);

					if (old != null && isRepeate(old.getCreateTime())) {// 重复了
																		// 30s未到。
						return new ResponseResult(0, "请等待30s之后再请求", null);
					}
					// 存入 数据库
					// 取值范围是[1000,10000)
					int random = (int) (Math.random() * 10000) + 1000;
					Sms sms = new Sms();
					sms.setCode(String.valueOf(random));
					sms.setCreateTime(new Date());
					sms.setPhone(phone);
					sms.setType(SmsType.Register.toString());
					baseService.update(sms);// 必须用update
					System.out.println("存入数据库了" + phone + ":" + random);
					System.out.println("发送注册验证码" + phone + ":" + random);
					if (smsSender.postSMS(random + "", phone)) {
						return new ResponseResult(1, "验证码已发送，稍等", null);
					} else {
						return new ResponseResult(1, "验证码发送有问题", null);
					}
				} else {
					return new ResponseResult(0, "用户已经注册", null);
				}
			case Getback:
				// 找回密码 验证码。。逻辑：
				/**
				 * ①先查看该用户是否已经注册，未注册返回 "未注册"。
				 * 
				 * ②已经注册了，就看上一次发送 验证码的时间， 如果在30s内，就警告。。
				 * 
				 * ③ 没有上述两个阻碍，就发送验证码
				 */
				if (user != null) {// 没有注册，ok 发送 验证码
					// 查看上一次 验证码是什么时候。
					Sms old = baseService.getById(Sms.class, phone);

					if (old != null && isRepeate(old.getCreateTime())) {// 重复了
																		// 30s未到。
						return new ResponseResult(0, "请等待30s之后再请求", null);
					}
					// 存入 数据库
					// 取值范围是[1000,10000)
					int random = (int) (Math.random() * 10000) + 1000;
					Sms sms = new Sms();
					sms.setCode(String.valueOf(random));
					sms.setCreateTime(new Date());
					sms.setPhone(phone);
					sms.setType(SmsType.Getback.toString());
					baseService.update(sms);
					System.out.println("存入getback数据库了" + phone + ":" + random);
					System.out.println("发送注册验证码" + phone + ":" + random);
					if (smsSender.postSMS(random + "", phone)) {
						return new ResponseResult(1, "验证码已发送，稍等", null);
					}else{
						return new ResponseResult(1, "验证码发送有问题", null);
					}
				} else {
					return new ResponseResult(0, "用户未注册，先去注册", null);
				}

			default:
				return new ResponseResult(0, "验证码类型错误", null);
			}


		} catch (Exception e) {
			e.printStackTrace();
			return new ResponseResult(0, "系统内部错误", null);
		}

	}

	/**
	 * {"res_code":"0","res_message":"Success","access_token":
	 * "b286febd2a8174a6ababcfe65800f07d1406709153083","expires_in":2592000}
	 * 有效期是30天，验证时间是2014年7月30号
	 * 
	 * @param random
	 * @return
	 * @throws Exception
	 */
	private boolean postSMS(int random, String phone) throws Exception {
		try {
			// 发送手机 短消息
			URL postUrl = new URL("http://api.189.cn/v2/emp/templateSms/sendSms");
			// 打开连接
			HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();
			// acceptor_tel=13123185312&template_id=91000001&template_param={"日报":"nihao","晚报":"nidao","url":"www.baidu.com"}&app_id=418839000000031xxx&access_token=c49fabf158e25985ed1284a75716a9b9137067210xxxx&timestamp=2013-09-06+16%3A07%3A42
			// http正文内，因此需要设为true
			connection.setDoOutput(true);
			// Read from the connection. Default is true.
			connection.setDoInput(true);
			// Set the post method. Default is GET
			connection.setRequestMethod("POST");
			// Post cannot use caches
			// Post 请求不能使用缓存
			connection.setUseCaches(false);
			// connection.setRequestProperty("Content-Type",
			// "application/json");
			// 要注意的是connection.getOutputStream会隐含的进行connect。
			connection.connect();
			DataOutputStream out = new DataOutputStream(connection.getOutputStream());
			// The URL-encoded contend
			// 正文，正文内容其实跟get的URL中'?'后的参数字符串一致
			// 2014-08-04+15%3A45%3A40
			DateTime dt = new DateTime();
			String time = dt.plusMinutes(2).toString("yyyy-MM-dd HH:mm:ss");
			// phone="15201967267";
			String content = "acceptor_tel=" + phone + "&template_id=91001874&template_param={\"param1\":\"Mr.Miss\",\"param2\":\"" + random + "\",\"param3\":\"30分钟\"}&app_id=201286860000036675&access_token=b286febd2a8174a6ababcfe65800f07d1406709153083&timestamp=" + time;

			// DataOutputStream.writeBytes将字符串中的16位的unicode字符以8位的字符形式写道流里面
			out.write(content.getBytes("utf-8"));

			out.flush();
			out.close(); // flush and close
			BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			String line;
			System.out.println("=============================");
			System.out.println("Contents of post request");
			System.out.println("=============================");
			while ((line = reader.readLine()) != null) {
				System.out.println(line);
			}
			System.out.println("=============================");
			System.out.println("Contents of post request ends");
			System.out.println("=============================");
			reader.close();
			connection.disconnect();
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
		// 成功{"res_code":"0","res_message":"Success","idertifier":"90610730164302806669"}
		// 成功{"res_code":"0","res_message":"Success","idertifier":"90610730165225806924"}
	}

	public static void main(String[] args) {
		try {
			ResponseResult rr = new ResponseResult();
			URL woTokenUrl = new URL("https://open.wo.com.cn/openapi/authenticate/v1.0?appKey=e9db452ac90dd1b98084a4c5bebac5087a2740fe&appSecret=691fb1fdb7bad07a9a74e74b2c667f6898d7c59c");
			HttpsURLConnection connection = (HttpsURLConnection) woTokenUrl.openConnection();
			// http正文内，因此需要设为true
			connection.setDoOutput(true);
			// Read from the connection. Default is true.
			connection.setDoInput(true);
			// Set the post method. Default is GET
			connection.setRequestMethod("GET");
			// 请求不能使用缓存
			connection.setUseCaches(false);
			// connection.setRequestProperty("Content-Type",
			// "application/json");
			// 要注意的是connection.getOutputStream会隐含的进行connect。
			connection.connect();

			BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			StringBuilder lines = new StringBuilder();
			String line;
			System.out.println("=============================");
			System.out.println("Contents of post request");
			System.out.println("=============================");
			while ((line = reader.readLine()) != null) {
				System.out.println(line);
				lines.append(line);
			}
			System.out.println("=============================");
			System.out.println("Contents of post request ends");
			System.out.println("=============================");
			reader.close();
			connection.disconnect();
		} catch (Exception e) {
			e.printStackTrace();

		}
	}

	private int expried = 10; // 过期时间 （10分钟）

	private boolean isExpired(Date date) {
		DateTime from = new DateTime(date);
		DateTime to = new DateTime(new Date());
		if (Minutes.minutesBetween(from, to).getMinutes() > expried) {
			return true;// 过期了
		}
		return false;
	}

	private int repeateTime = 30; // 30秒 发一次

	private boolean isRepeate(Date date) {
		DateTime from = new DateTime(date);
		DateTime to = new DateTime(new Date());
		// 间隔 小于 30秒
		if (Seconds.secondsBetween(from, to).getSeconds() < repeateTime) {
			return true;// 重复了
		}
		return false;
	}
}
